#ifndef CH423_H
#define CH423_H
#ifdef ARDUINO

#include <Arduino.h>

#elif defined(USE_FULL_LL_DRIVER)

#include <I2C_Driver.h>

extern void delay(uint16_t num);

#else
#include <stm32f1xx_hal.h>
#define delay HAL_Delay
#endif

#include <Wire.h>
#include "plc_error.h"
#include "nStream.h"

#define DELAY_0_1US \
    {               \
        _nop_();    \
    }                       // MCS51>10MHz
#define CH423_SYSON1 0x0417 //开启自动扫描显示
// CH423接口定义
#define CH423_I2C_ADDR1 0x40 >> 1 // CH423的地址
#define CH423_I2C_MASK 0x3E       // CH423的高字节命令掩码

/*  设置系统参数命令 */

#define CH423_SYS_CMD 0x4800 // 设置系统参数命令，默认方式
#define BIT_X_INT 0x08       // 使能输入电平变化中断，为0禁止输入电平变化中断；为1并且DEC_H为0允许输出电平变化中断
#define BIT_DEC_H 0x04       // 控制开漏输出引脚高8位的片选译码
#define BIT_DEC_L 0x02       // 控制开漏输出引脚低8位的片选译码
#define BIT_IO_OE 0x01       // 控制双向输入输出引脚的三态输出，为1允许输出

/*  设置低8位开漏输出命令 */

#define CH423_OC_L_CMD 0x4400 // 设置低8位开漏输出命令，默认方式
#define BIT_OC0_L_DAT 0x01    // OC0为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC1_L_DAT 0x02    // OC1为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC2_L_DAT 0x04    // OC2为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC3_L_DAT 0x08    // OC3为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC4_L_DAT 0x10    // OC4为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC5_L_DAT 0x20    // OC5为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC6_L_DAT 0x40    // OC6为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC7_L_DAT 0x80    // OC7为0则使引脚输出低电平，为1则引脚不输出

/*  设置高8位开漏输出命令 */

#define CH423_OC_H_CMD 0x4600 // 设置低8位开漏输出命令，默认方式
#define BIT_OC8_L_DAT 0x01    // OC8为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC9_L_DAT 0x02    // OC9为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC10_L_DAT 0x04   // OC10为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC11_L_DAT 0x08   // OC11为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC12_L_DAT 0x10   // OC12为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC13_L_DAT 0x20   // OC13为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC14_L_DAT 0x40   // OC14为0则使引脚输出低电平，为1则引脚不输出
#define BIT_OC15_L_DAT 0x80   // OC15为0则使引脚输出低电平，为1则引脚不输出

/* 设置双向输入输出命令 */

#define CH423_SET_IO_CMD 0x6000 // 设置双向输入输出命令，默认方式
#define BIT_IO0_DAT 0x01        // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO0为0输出低电平，为1输出高电平
#define BIT_IO1_DAT 0x02        // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO1为0输出低电平，为1输出高电平
#define BIT_IO2_DAT 0x04        // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO2为0输出低电平，为1输出高电平
#define BIT_IO3_DAT 0x08        // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO3为0输出低电平，为1输出高电平
#define BIT_IO4_DAT 0x10        // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO4为0输出低电平，为1输出高电平
#define BIT_IO5_DAT 0x20        // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO5为0输出低电平，为1输出高电平
#define BIT_IO6_DAT 0x40        // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO6为0输出低电平，为1输出高电平
#define BIT_IO7_DAT 0x80        // 写入双向输入输出引脚的输出寄存器，当IO_OE=1,IO7为0输出低电平，为1输出高电平

/* 读取双向输入输出命令 */

#define CH423_RD_IO_CMD 0x4D // 输入I/O引脚当前状态

extern const unsigned char BCD_decode_tab[];
extern const char ASCII[80][8];

class ch423 {
public:
    ch423(nStream *Wire);

    void begin() {
        wire->begin(01);
        clear();
        CH423_Write(4, 0x17); // 开启显示
    }

    void clear()
    {
        for (int i = 0; i < 16; i++)
            CH423_buf_index(i, 0); // 因为CH423复位时不清空显示内容，所以刚开电后必须人为清空，再开显示
    }

    // void CH423_WriteByte(unsigned short cmd); // 写出数据
    unsigned char CH423_ReadByte(); // 读取数据

    // 特定用途子程序
    void CH423_Write(unsigned char addr, unsigned char cmd);

    void CH423_buf_write(unsigned char addr, unsigned char cmd) // 向CH423输出数据或者操作命令，自动建立数据映象
    {
        // 加载数据的命令,需要备份数据到映象缓冲区
        CH423_buf[addr] = (unsigned char)(cmd & 0xFF); // 备份数据到相应的映象单元

        CH423_Write(addr, cmd); // 发出
    }

    void CH423_buf_index(unsigned char index, unsigned char dat) // 向CH423指定的数码管输出数据,自动建立数据映象
    {                                                            // index 为数码管序号,有效值为0到15,分别对应DIG0到DIG15
        // unsigned short cmd;
        CH423_buf[index] = dat; // 备份数据到相应的映象单元
        // cmd = (CH423_DIG0 + ((unsigned short)index << 8)) | dat; // 生成操作命令
        CH423_Write(0x10 + index, dat); // 发出
    }

    void CH423_set_bit(unsigned char bit_addr) // 段位点亮
    {
        unsigned char byte_addr;
        byte_addr = (bit_addr >> 3) & 0x0F;
        CH423_buf_index(byte_addr, CH423_buf[byte_addr] | (1 << (bit_addr & 0x07)));
    }

    void CH423_clr_bit(unsigned char bit_addr) // 段位熄灭
    {
        unsigned char byte_addr;
        byte_addr = (bit_addr >> 3) & 0x0F;
        CH423_buf_index(byte_addr, CH423_buf[byte_addr] & ~(1 << (bit_addr & 0x07)));
    }

    void setBit(uint8_t row, uint8_t col, uint8_t val)
    {
        CH423_buf[row] &= ~(1 << col);
        CH423_buf[row + 8] &= ~(1 << col);
        if (val & 1)
        {
            CH423_buf[row] |= (1 << col);
        }
        if (val & 2)
        {
            CH423_buf[row + 8] |= (1 << col);
        }
    }

    void setChar(const char chr, uint8_t color)
    {
        const char *dat = ASCII[chr - 0x30];
        for (int i = 0; i < 8; i++)
        {
            char b = 0, c = dat[i];
            for (int j = 0; j < 8; j++)
            {
                b <<= 1;
                if (c & 1)
                    b += 1;
                c >>= 1;
            }
            if (color & 1)
                CH423_buf[i] = b;
            else
                CH423_buf[i] = 0;
            if (color & 2)
                CH423_buf[i + 8] = b;
            else
                CH423_buf[i + 8] = 0;
        }
        update();
    }

    void update()
    {
        for (int i = 0; i < 16; i++)
            CH423_Write(0x10 + i, CH423_buf[i]);
    }

    void LEFTMOV() // 左移
    {
        unsigned short i;
        for (i = 0x000F; i >= 0x0001; i--)
        {
            CH423_buf_index(i, CH423_buf[i - 1]);
        }
        CH423_Write(0x10, 00);
    }

    void LEFTCYC() // 左循
    {
        unsigned short i, j;
        j = CH423_buf[15];
        for (i = 0x000F; i >= 0x0001; i--)
        {
            CH423_buf_index(i, CH423_buf[i - 1]);
        }
        CH423_Write(0x10, j);
    }

    void RIGHTMOV() // 右移
    {
        unsigned short i;
        for (i = 0x0000; i < 0x000F; i++)
        {
            CH423_buf_index(i, CH423_buf[i + 1]);
        }
        CH423_Write(0x1F, 00);
    }

    void RIGHTCYC() // 右循
    {
        unsigned char i, j;
        j = CH423_buf[0];
        for (i = 0x0000; i < 0x000F; i++)
        {
            CH423_buf_index(i, CH423_buf[i + 1]);
        }
        CH423_Write(0x1F, j);
    }

    void TWINKLE(unsigned char dig_number) // 闪烁
    {
        unsigned char old;
        old = CH423_buf[dig_number & 0x0F];
        CH423_buf_index(dig_number, 0x00);
        delay(250);
        CH423_buf_index(dig_number, old);
        delay(250);
    }

    void ch423_test()
    {
        unsigned char i, j;
        // Delay( 50 );
        for (i = 0; i < 16; i++)
            CH423_buf_index(i, 0); // 因为CH423复位时不清空显示内容，所以刚开电后必须人为清空，再开显示
        CH423_Write(4, 0x17);      // 开启显示

        while (1)
        { // 演示
            for (i = 0; i < 16; i++)
                CH423_buf_index(i, 0xFF); // 全亮
            // mDelayS( 1 );
            for (i = 0; i < 16; i++)
                CH423_buf_index(i, 0x00); // 全灭
            delay(500);
            for (j = 0; j != 8; j++)
            { // 依次扫描每段，演示
                for (i = 0; i != 16; i++)
                    CH423_buf_index(i, 1 << j);
                delay(250);
            }
            for (i = 0; i < 16; i++)
                CH423_buf_index(i, 0xFF); // 全亮
            delay(500);
            // for (i = 0; i < 16; i++)
            //     CH423_buf_index(i, BCD_decode_tab[i]); // 依次显示BCD译码0、1、2、3、4、5、6、7、8、9、A、b、c、d、E、F
            // delay(500);
            for (i = 0; i != 8; i++) // 左循环
            {
                LEFTCYC();
                delay(250);
            }
            for (i = 0; i != 8; i++) // 右循环
            {
                RIGHTCYC();
                delay(250);
            }
            for (i = 0; i != 8; i++) // 左移
            {
                LEFTMOV();
                delay(250);
            }
            for (i = 0; i != 8; i++) // 右移
            {
                RIGHTMOV();
                delay(250);
            }
            // for (i = 0; i < 16; i++)
            //     CH423_buf_index(i, BCD_decode_tab[i]); // 依次显示BCD译码0、1、2、3、4、5、6、7、8、9、a、b、c、d、E、F
            // delay(250);
            for (i = 0; i != 16; i++) // 闪烁
            {
                TWINKLE(i);
                delay(100);
            }
            for (i = 0; i < 16; i++)
                CH423_buf_index(i, 0x00); // 全灭
            delay(250);
            for (i = 0; i != 16 * 8; i++) // 依次置段位
            {
                CH423_set_bit(i);
                delay(100);
            }
            delay(500);
            for (i = 0; i != 16 * 8; i++) // 依次清段位
            {
                CH423_clr_bit(i);
                delay(100);
            }
            delay(500);
        }
    }

private:
    nStream *wire;
    unsigned char CH423_buf[16]; //定义16个数码管的数据映象缓存区
};

#endif
